package com.xiyu.service.service.material;

import com.xiyu.service.model.material.order.MaterialPurchaseDetail;
import com.xiyu.service.vo.material.purchase.PurchaseSingleGetDetailOutput;
import com.xiyu.service.enums.material.order.MaterialPurchaseStatusEnum;
import com.xiyu.service.model.material.order.*;
import com.xiyu.service.service.infra.data.DictNoService;
import com.xiyu.service.vo.material.purchase.PurchaseSingleGetOutput;
import com.xiyu.service.vo.material.purchase.PurchaseUpdateStatusInput;
import com.xiyu.service.repository.material.order.MaterialPurchaseDetailRepository;
import com.xiyu.service.vo.material.purchase.PurchaseUpdateInput;
import com.xiyu.service.vo.material.purchase.PurchaseCreateInput;
import com.xiyu.service.repository.material.order.MaterialPurchaseRepository;
import com.xiyu.service.vo.material.purchase.PurchasePageQueryOutput;
import com.xiyu.service.vo.material.purchase.PurchasePageQueryInput;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.babyfish.jimmer.Page;
import java.util.*;

import org.springframework.transaction.annotation.Transactional;

import java.util.stream.Collectors;

import com.xiyu.service.framework.web.web.core.pojo.PageResult;
import com.xiyu.service.convert.material.PurchaseConvert;
import com.xiyu.service.util.entity.EntityUtils;
import org.babyfish.jimmer.DraftObjects;

import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;
import static com.xiyu.service.errorCode.material.PurchaseErrorCode.*;

/**
 * 采购单管理 Service 实现类
 */
@Service
@Validated
public class PurchaseServiceImpl implements PurchaseService {

    @Resource
    private MaterialPurchaseDetailRepository materialPurchaseDetailRepository;

    @Resource
    private MaterialPurchaseRepository materialPurchaseRepository;

    @Resource
    private DictNoService dictNoService;


    @Override
    public PageResult<PurchasePageQueryOutput> pageQuery(PurchasePageQueryInput inputVO) {
        Page<MaterialPurchase> pageMaterialPurchase = materialPurchaseRepository.pageQuery(inputVO);
        List<PurchasePageQueryOutput> listMaterialPurchase = PurchaseConvert.INSTANCE.pageQueryOutputConvert(pageMaterialPurchase.getRows());
        return new PageResult<>(listMaterialPurchase, pageMaterialPurchase.getTotalRowCount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(PurchaseCreateInput inputVO) {
        MaterialPurchase newMaterialPurchase = PurchaseConvert.INSTANCE.createInputConvert(inputVO);
        List<MaterialPurchaseDetail> details = newMaterialPurchase.details();
        if(details.isEmpty()){
            throw exception(MATERIAL_PURCHASE_DETAIL_NOT_EMPTY);
        }
        newMaterialPurchase = MaterialPurchaseDraft.$.produce(newMaterialPurchase, draft -> {
            draft.setPurchaseNo(dictNoService.produceNo("purchase"))
                    .setStatus(MaterialPurchaseStatusEnum.PENDING_REVIEW.getValue());
            DraftObjects.unload(draft, MaterialPurchaseProps.DETAILS);
        });
        newMaterialPurchase = materialPurchaseRepository.insert(newMaterialPurchase);
        MaterialPurchase finalNewMaterialPurchase1 = newMaterialPurchase;
        details = details.stream().map(detail-> MaterialPurchaseDetailDraft.$.produce(detail, draft -> {
          draft.setPurchaseId(finalNewMaterialPurchase1.id());
        })).collect(Collectors.toList());
        materialPurchaseDetailRepository.saveAll(details);
        return newMaterialPurchase.id();

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(PurchaseUpdateInput inputVO) {
        Optional<MaterialPurchase> optionalOldMaterialPurchase = materialPurchaseRepository.findById(inputVO.getId());
        if(!optionalOldMaterialPurchase.isPresent()){
            throw exception(MATERIAL_PURCHASE_NOT_EXIST);
        }
        for(PurchaseUpdateInput.detail detail : inputVO.getDetails()){
            if(Objects.equals(detail.getOperateType(), "delete")){
                Optional<MaterialPurchaseDetail> optionalDeleteMaterialPurchaseDetail = materialPurchaseDetailRepository.findById(detail.getId());
                if(!optionalDeleteMaterialPurchaseDetail.isPresent()){
                    throw exception(MATERIAL_PURCHASE_DETAIL_NOT_EXIST);
                }
                materialPurchaseDetailRepository.deleteById(detail.getId());
            }else if(Objects.equals(detail.getOperateType(), "new")){
                MaterialPurchaseDetail newMaterialPurchaseDetail = PurchaseConvert.INSTANCE.updateInputConvert(detail);
                materialPurchaseDetailRepository.insert(newMaterialPurchaseDetail);
            }else{
                Optional<MaterialPurchaseDetail> optionalOldMaterialPurchaseDetail = materialPurchaseDetailRepository.findById(detail.getId());
                if(!optionalOldMaterialPurchaseDetail.isPresent()) {
                    throw exception(MATERIAL_PURCHASE_DETAIL_NOT_EXIST);
                }
                MaterialPurchaseDetail updateMaterialPurchaseDetail = PurchaseConvert.INSTANCE.updateInputConvert(detail);
                if (!EntityUtils.isEquals(optionalOldMaterialPurchaseDetail.get(), updateMaterialPurchaseDetail))
                    materialPurchaseDetailRepository.update(updateMaterialPurchaseDetail);
            }
        }
        if(materialPurchaseDetailRepository.findByPurchaseId(inputVO.getId()).isEmpty()){
            throw exception(MATERIAL_PURCHASE_DETAIL_NOT_EMPTY);
        }
        MaterialPurchase updateMaterialPurchase = PurchaseConvert.INSTANCE.updateInputConvert(inputVO);
        updateMaterialPurchase = MaterialPurchaseDraft.$.produce(updateMaterialPurchase, draft -> {
            DraftObjects.unload(draft, MaterialPurchaseProps.DETAILS);
        });
        if (!EntityUtils.isEquals(optionalOldMaterialPurchase.get(), updateMaterialPurchase))
            materialPurchaseRepository.update(updateMaterialPurchase);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateStatus(PurchaseUpdateStatusInput inputVO) {
        Optional<MaterialPurchase> optionalMaterialPurchase = materialPurchaseRepository.findById(inputVO.getId());
        if(!optionalMaterialPurchase.isPresent()){
            throw exception(MATERIAL_PURCHASE_NOT_EXIST);
        }
        MaterialPurchase materialPurchase = optionalMaterialPurchase.get();

        switch (inputVO.getAction()) {
            case "review":
                // 审核采购单
                if (!materialPurchase.status().equals(MaterialPurchaseStatusEnum.PENDING_REVIEW.getValue())) {
                    throw exception(MATERIAL_PURCHASE_STATUS_NOT_PENDING_REVIEW);
                }
                materialPurchase = MaterialPurchaseDraft.$.produce(materialPurchase, draft -> {
                    draft.setStatus(MaterialPurchaseStatusEnum.PENDING_IN.getValue());
                });
                break;
            case "cancel":
                // 取消采购单
                if (!materialPurchase.status().equals(MaterialPurchaseStatusEnum.PENDING_REVIEW.getValue())) {
                    throw exception(MATERIAL_PURCHASE_STATUS_NOT_PENDING_REVIEW);
                }
                materialPurchase = MaterialPurchaseDraft.$.produce(materialPurchase, draft -> {
                    draft.setStatus(MaterialPurchaseStatusEnum.CANCEL.getValue());
                });
                break;
            case "revertReview":
                // 驳回审核采购单
                if (!materialPurchase.status().equals(MaterialPurchaseStatusEnum.PENDING_IN.getValue())) {
                    throw exception(MATERIAL_PURCHASE_STATUS_NOT_PENDING_IN);
                }
                materialPurchase = MaterialPurchaseDraft.$.produce(materialPurchase, draft -> {
                    draft.setStatus(MaterialPurchaseStatusEnum.PENDING_REVIEW.getValue());
                });
                break;
            case "stop":
                // 停止入库
                if (!materialPurchase.status().equals(MaterialPurchaseStatusEnum.PARTIAL_IN.getValue())) {
                    throw exception(MATERIAL_PURCHASE_STATUS_NOT_PARTIAL_IN);
                }
                materialPurchase = MaterialPurchaseDraft.$.produce(materialPurchase, draft -> {
                    draft.setStatus(MaterialPurchaseStatusEnum.COMPLETED.getValue());
                });
                break;
        }

        materialPurchaseRepository.update(materialPurchase);
        return true;
    }

    @Override
    public PurchaseSingleGetOutput singleGet(Long id) {
        Optional<MaterialPurchase> optionalMaterialPurchase = materialPurchaseRepository.findDetailById(id);
        if(!optionalMaterialPurchase.isPresent()){
            throw exception(MATERIAL_PURCHASE_NOT_EXIST);
        }
        return PurchaseConvert.INSTANCE.singleGetOutputConvert(optionalMaterialPurchase.get());
    }

    @Override
    public List<PurchaseSingleGetDetailOutput> singleGetDetail(Long id) {
        List<MaterialPurchaseDetail> optionMaterialPurchaseDetail = materialPurchaseDetailRepository.findDetailByPurchaseId(id);
        return PurchaseConvert.INSTANCE.singleGetDetailOutputConvert(optionMaterialPurchaseDetail);
    }

}
